/**
 *  @file IHttpBody.h
 */
#ifndef _IHTTPBODY_H_
#define _IHTTPBODY_H_

#include "../../../common/BaseObject.h"
#include "../../../text/CFString.h"
#include "../../../collections/BinBuf.h"
#include "../../../collections/KeyValueListT.h"
#include "../../../collections/ArrayT.h"
#include "../../../io/Stream.h"

namespace cppflib
{

namespace io { class FileStream; 
               class MemoryStream; }
namespace util { namespace zip { class Inflater; } }

namespace net
{

class Socket;
class SocketStream;

namespace http
{

namespace message
{

/**
 *  Base class to wrap around different kinds of body sent to/received from peer
 */
class _DLLAPI IHttpBody : public io::ReadStream
{
private:
    CFString contentType;
    u64_t contentLength;
    u64_t curReadPos;
    bool isChunked;
    bool isCompressed;

protected:
    u64_t GetCurReadPos() { return curReadPos; }
    void SetCurReadPos(u64_t pos) { curReadPos = pos; }
    void AdvanceReadPos(sz_t len) { curReadPos += len; }
    void SetContentLength(u64_t len) { contentLength = len; }
    void __SetChunked(bool b) { isChunked = b; }
    void __SetCompressed(bool b) { isCompressed = b; }

public:
    IHttpBody(void) 
    {
       SetContentType(_S("application/octet-stream"));
       contentLength = 0;
       curReadPos = 0;
       isChunked = false;
       isCompressed = false;
    }

    virtual ~IHttpBody(void) { }

    sz_t GetBytesReceived() { return static_cast<sz_t>(GetCurReadPos()); }

    CFString & GetContentType(void) { return this->contentType; }
    void SetContentType(pcwstr_t contentType) { this->contentType = contentType; }
    
    /**
     *  Send/receive chunked data or not
     */
    bool IsChunked(void) { return isChunked; }

    /**
     *  Set true if user want to send this body in chunk
     */
    virtual void SetChunked(bool b) { __SetChunked(b); }

    /**
     *  Send/receive compressed data or not
     */
    bool IsCompressed(void) { return isCompressed; }

    /**
     *  Set true if user want to send this body in gzip format
     */
    virtual void SetCompressed(bool b) { __SetCompressed(b); }

    /**
     *  Derived class may want to override this
     */
    virtual u64_t GetContentLength(void) { return this->contentLength; }

    
    /**
     *  From ReadStream
     */
    virtual i64_t Seek(i64_t offset, seekorigin_t origin) { return 0; }
    /**
     *  From ReadStream
     */
    virtual void Close() { }
};


/**
 *  Upload a file to server
 */
class _DLLAPI FileBody : public IHttpBody
{
private:
   CFString filePath;
   io::FileStream *pStream;

private:
   FileBody(void);
   void OpenFile();
   void CloseFile();

public:
   explicit FileBody(pcwstr_t filePath);
   virtual ~FileBody(void);

   virtual u64_t GetContentLength(void);

   virtual int Read(collections::ArrayByte &buffer);
   virtual int Read(collections::ArrayByte &buffer, int offset, int count);
   virtual int Read(); 
   virtual void Close();
};

/**
 *  Upload a plain string (in UTF-8)
 */
class _DLLAPI StringBody : public IHttpBody
{
private:
   CFString *pInStr;
   char *pStr;

private:
   StringBody(void);

public:
   explicit StringBody(CFString * pInStr);
   virtual ~StringBody(void);

   virtual int Read(collections::ArrayByte &buffer);
   virtual int Read(collections::ArrayByte &buffer, int offset, int count);
   virtual int Read(); 
};

/**
 *  Upload a in-memory byte stream to server
 */
class _DLLAPI ByteStreamBody : public IHttpBody
{
private:
   BaseObject *pByteBody;

private:
   ByteStreamBody(void);

public:
   explicit ByteStreamBody(collections::BinBuf *pBinBuf);
   explicit ByteStreamBody(io::MemoryStream *pMemStream);
   explicit ByteStreamBody(collections::ArrayByte *pArrayByte);
   virtual ~ByteStreamBody(void);

   virtual int Read(collections::ArrayByte &buffer);
   virtual int Read(collections::ArrayByte &buffer, int offset, int count);
   virtual int Read(); 
};

/**
 *  Upload a URL encoded string to server
 */
class _DLLAPI UrlEncodedBody : public IHttpBody
{
private:
   CFString encodedStr;
   char *pEncodedStr;

private:
   UrlEncodedBody(void);

public:
   explicit UrlEncodedBody(const collections::NameValueList &keyValueParameters);
   virtual ~UrlEncodedBody(void);
   
   virtual int Read(collections::ArrayByte &buffer);
   virtual int Read(collections::ArrayByte &buffer, int offset, int count);
   virtual int Read();
};


/**
 *  To fetch HTTP body from peer
 */
class _DLLAPI HttpPeerBody : public IHttpBody
{
private:
   io::Stream *pStream;
   util::zip::Inflater *pInflater;
   collections::ArrayByte *pSingleByteBuffer;

private:
   HttpPeerBody(void);
   void PrepareBuffer();
   
public:
   HttpPeerBody(io::Stream *pStream, u64_t contentLength, bool isChunked, bool isCompressed);
   virtual ~HttpPeerBody(void);

   virtual void SetChunked(bool b) { }     // override to do nothing
   virtual void SetCompressed(bool b) { }  // override to do nothing

   virtual int Read(collections::ArrayByte &buffer);
   virtual int Read(collections::ArrayByte &buffer, int offset, int count);
   virtual int Read();
};

} // end of namespace message

} // end of namespace http

} // end of namespace net

} // end of namespace cppflib


#endif
